#include "bsp.h"
#include "mrfi.h"
#include "nwk_types.h"
#include "nwk_api.h"
#include "bsp_leds.h"
#include "bsp_buttons.h"
#include "nwk_pll.h"

#ifndef APP_AUTO_ACK
	#error ERROR: Must define the macro APP_AUTO_ACK for this application.
#endif

void toggleLED(uint8_t);
void linkTo(void);
void createRandomAddress(addr_t * lAddr);

static uint8_t  sTid = 0;
static linkID_t sLinkID1 = 0;

#define SPIN_ABOUT_A_SECOND				NWK_DELAY(1000)
#define SPIN_ABOUT_A_QUARTER_SECOND		NWK_DELAY(250)

/* How many times to try a Tx and miss an acknowledge before doing a scan */
#define MISSES_IN_A_ROW  2

int main (void)
{
	BSP_Init();

	/* If an on-the-fly device address is generated it must be done before the
	 * call to SMPL_Init(). If the address is set here the ROM value will not
	 * be used. If SMPL_Init() runs before this IOCTL is used the IOCTL call
	 * will not take effect. One shot only. The IOCTL call below is conformal.
	 */
#ifdef I_WANT_TO_CHANGE_DEFAULT_ROM_DEVICE_ADDRESS_PSEUDO_CODE
	{
		addr_t lAddr;
		createRandomAddress(&lAddr);
		SMPL_Ioctl(IOCTL_OBJ_ADDR, IOCTL_ACT_SET, &lAddr);
	}
#endif /* I_WANT_TO_CHANGE_DEFAULT_ROM_DEVICE_ADDRESS_PSEUDO_CODE */

	/* Keep trying to join (a side effect of successful initialization) until
	 * successful. Toggle LEDS to indicate that joining has not occurred.
	 */
	while (SMPL_SUCCESS != SMPL_Init(NULL))
	{
		toggleLED(1);
		toggleLED(2);
		SPIN_ABOUT_A_SECOND; /* calls nwk_pllBackgrounder for us */
	}

	/* LEDs on solid to indicate successful join. */
	if (!BSP_LED2_IS_ON())	{	toggleLED(2);	}
	if (!BSP_LED1_IS_ON())	{	toggleLED(1);	}

	/* Unconditional link to AP which is listening due to successful join. */
	linkTo();

	while (1)
		FHSS_ACTIVE( nwk_pllBackgrounder( false ) );
}

void linkTo()
{
	uint8_t     msg[2];
	uint8_t     button, misses, done;

	/* Keep trying to link... */
	while (SMPL_SUCCESS != SMPL_Link(&sLinkID1))
	{
		toggleLED(1);
		toggleLED(2);
		SPIN_ABOUT_A_SECOND; /* calls nwk_pllBackgrounder for us */
	}

	/* Turn off LEDs. */
	if (BSP_LED2_IS_ON())	{	toggleLED(2);	}
	if (BSP_LED1_IS_ON())	{	toggleLED(1);	}

#ifndef FREQUENCY_HOPPING
	/* sleep until button press... */
	SMPL_Ioctl( IOCTL_OBJ_RADIO, IOCTL_ACT_RADIO_SLEEP, 0);
#endif

	while (1)
	{
		/* keep the FHSS scheduler happy */
		FHSS_ACTIVE( nwk_pllBackgrounder( false ) );
    
		button = 0;
		/* Send a message when either button pressed */
		if (BSP_BUTTON1())
		{	/* calls nwk_pllBackgrounder for us */
			SPIN_ABOUT_A_QUARTER_SECOND;  /* debounce... */
			/* Message to toggle LED 1. */
			button = 1;
		}
		else if (BSP_BUTTON2())
		{	/* calls nwk_pllBackgrounder for us */
			SPIN_ABOUT_A_QUARTER_SECOND;  /* debounce... */
			/* Message to toggle LED 2. */
			button = 2;
		}
		if (button)
		{
			uint8_t      noAck;
			smplStatus_t rc;

#ifndef FREQUENCY_HOPPING
			/* get radio ready...awakens in idle state */
			SMPL_Ioctl( IOCTL_OBJ_RADIO, IOCTL_ACT_RADIO_AWAKE, 0);
#endif

			/* Set TID and designate which LED to toggle */
			msg[1] = ++sTid;
			msg[0] = (button == 1) ? 1 : 2;
			done = 0;
			while (!done)
			{
				noAck = 0;

				/* Try sending message MISSES_IN_A_ROW times looking for ack */
				for (misses=0; misses < MISSES_IN_A_ROW; ++misses)
				{
					if (SMPL_SUCCESS == (rc = SMPL_SendOpt(sLinkID1, msg, sizeof(msg), SMPL_TXOPTION_ACKREQ)))
					{
						/* Message acked. We're done. Toggle LED 1 to indicate ack received. */
						toggleLED(1);
						break;
					}
					if (SMPL_NO_ACK == rc)
					{
						/* Count ack failures. Could also fail becuase of CCA and
						 * we don't want to scan in this case.
						 */
						noAck++;
					}
				}
				if (MISSES_IN_A_ROW == noAck)
				{
					/* Message not acked. Toggle LED 2. */
					toggleLED(2);
#ifdef FREQUENCY_AGILITY
					/* Assume we're on the wrong channel so look for channel by
					 * using the Ping to initiate a scan when it gets no reply. With
					 * a successful ping try sending the message again. Otherwise,
					 * for any error we get we will wait until the next button
					 * press to try again.
					 */
					if (SMPL_SUCCESS != SMPL_Ping(sLinkID1))
					{
						done = 1;
					}
#else
					done = 1;
#endif  /* FREQUENCY_AGILITY */
				}
				else
				{
					/* Got the ack or we don't care. We're done. */
					done = 1;
				}
			}

#ifndef FREQUENCY_HOPPING
			/* radio back to sleep */
			SMPL_Ioctl( IOCTL_OBJ_RADIO, IOCTL_ACT_RADIO_SLEEP, 0);
#endif
		}
	}
}


void toggleLED(uint8_t which)
{
	if (1 == which)			{	BSP_TOGGLE_LED1();	}
	else if (2 == which)	{	BSP_TOGGLE_LED2();	}
	return;
}

